package commons.jdbc.basic;





import com.alibaba.fastjson.JSON;
import commons.jdbc.reflect.ReflectEntityField;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 工具单表操作 全部实现了  多表才需要 自己写 sql
 * @param <T>
 */

public class BasicDao<T> {
	JDBCUtil dbUtil ;

/*	private  Class<T>  cl;
	public  BasicDao(){
		ParameterizedType parameterizedType =(ParameterizedType) this.getClass().getGenericSuperclass();
		cl =(Class<T>) parameterizedType.getActualTypeArguments()[0];
		System.out.println(cl);
	}*/
	/**
	 * 打开事务
	 */
	public void openTransaction() {
		dbUtil= new JDBCUtil();
		try {
			dbUtil.isAutoCommit=false;
			// 设置不自动提交
			dbUtil.getConnection().setAutoCommit(dbUtil.isAutoCommit);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 手动提交
	 */
	public void commit() {
		try {
			dbUtil.isAutoCommit=true;
			dbUtil.getConnection().commit();
			dbUtil.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 事务滚回
	 */
	public void rollback() {
		try {
			dbUtil.isAutoCommit=true;
			dbUtil.getConnection().rollback();
			dbUtil.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 查询表所有数据
	 * @param cl
	 * @return
	 */
	public List<T> findListBean(Class<T> cl) {
		// 获得表名
		String tableName = ReflectEntityField.getTableName(cl);
		//System.out.println("表名：" + tableName);
		if (tableName != null) {
			String sql = "select * from " + tableName;
			//System.out.println("sql语句:" + sql);
			return findListBean(sql, cl);
		}
		return null;
	}

	/**
	 * 查询所有数据 先把每一行数据封装成对象 然后再所有打包成集合 前提条件 实体类 属性名与表名的字段 必须一致
	 *
	 * @param sql
	 *            查询sql
	 * @param cl
	 *            要封装的类型
	 * @return 返回集合结果
	 */
	public List<T> findListBean(String sql, Class<T> cl) {
		return findListBeanWhere(sql, cl, null);
	}
	/**
	 * 查询所有数据 先把每一行数据封装成对象 然后再所有打包成集合 前提条件 实体类 属性名与表名的字段 必须一致
	 *
	 * @param sql
	 *            查询sql
	 * @param cl
	 *            要封装的类型
	 * @return 返回集合结果
	 */
	public <O> List<O> findListBeanOther(String sql, Class<O> cl) {
		return findListBeanWhereOther(sql, cl, null);
	}

	/**

	/**
	 * 有条件的查询 先把每一行数据封装成对象 然后再所有打包成集合
	 前提条件 实体类 属性名与表名的字段 必须一致
	 *
	 * @param sql
	 * @param cl
	 * @param params
	 * @return
	 */
	public  List<T> findListBeanWhere(String sql, Class<T> cl, Object... params) {
		//封装查询结果
		List<T> list = new ArrayList<T>();
		dbUtil= new JDBCUtil();
		// 先判断是否有下一个数据 一行数据就是一个对象
		for (Map<String,Object> map:dbUtil.query(sql, params)) {
			//System.out.println("内容:" + getEntity(map, cl));
			// 获得这一行数据
			list.add(getEntity(map, cl));
			//System.out.println("list:" + list);
		}


		return list;
	}
	public <O>  List<O> findListBeanWhereOther(String sql, Class<O> cl, Object... params) {
		//封装查询结果
		List<O> list = new ArrayList<O>();
		dbUtil= new JDBCUtil();
		// 先判断是否有下一个数据 一行数据就是一个对象
		for (Map<String,Object> map:dbUtil.query(sql, params)) {
			//System.out.println("内容:" + getEntity(map, cl));
			if(cl.getName().startsWith("java.lang") || cl.getName().startsWith("java.util.Date")){
				O o = getLangOrDate(map, cl);
				if(o!=null)
				  list.add(o);
			}else {
				// 获得这一行数据
				list.add(getEntityOther(map, cl));
			}
			//System.out.println("list:" + list);
		}


		return list;
	}


	// select name, count(id) from admin group by name=List<Object[]>
	/**
	 * 查询多个字段 但不能封装成实体
	 *
	 * @param sql
	 * @return
	 */
	public List<Object[]> findListArray(String sql) {
		return findListArrayWhere(sql, null);
	}

	/**
	 * 根据条件查询多个字段 但不能封装成实体
	 *
	 * @param sql
	 * @param params
	 * @return
	 */
	public List<Object[]> findListArrayWhere(String sql, Object... params) {
		//封装查询结果
		List<Object[]> list = new ArrayList<>();
		dbUtil= new JDBCUtil();
		// 先判断是否有下一个数据 一行数据就是一个对象          	// 获得查询结果集
		for (Map<String,Object> map:dbUtil.query(sql, params)) {
			// 获得这一行数据
			list.add(getArrayObject(map));
			//System.out.println("list:" + list);
		}
		return list;
	}

	/**
	 * 根据条件查询一条数据多个字段 但不能封装成实体
	 *
	 * @param sql
	 * @param params
	 * @return
	 */
	public Object[] findArray(String sql, Object... params) {
		dbUtil= new JDBCUtil();
		//封装查询结果
		List<Map<String, Object>> query = dbUtil.query(sql, params);
		return getArrayObject(query.get(0));
	}

	/**
	 * 根据主键修改
	 *
	 * @return
	 */
	public T updatePrimaryKey(T t) {
		StringBuffer sql = new StringBuffer();
		Object[] parameters = null;
		// 获得表名
		String tableName = ReflectEntityField.getTableName(t.getClass());
		String primaryKeyName = ReflectEntityField.getPrimaryKeyName(t.getClass());
		Map<String, Object> columnNameAndValue = ReflectEntityField.getColumnValue(t);
		//System.out.println(columnNameAndValue.size());
		//System.out.println(tableName + ":" + primaryKeyName + columnNameAndValue);
		if (tableName != null && primaryKeyName != null && columnNameAndValue.size() > 0
				&& columnNameAndValue.get(primaryKeyName) != null) {
			sql.append("update  " + tableName + " set ");
			int index = 0;
			parameters = new Object[columnNameAndValue.size()];
			//System.out.println(parameters.length+":"+columnNameAndValue.size());
			for (String columnName : columnNameAndValue.keySet()) {
				if (!columnName.equals(primaryKeyName)) {
					sql.append(columnName + "=?,");
					parameters[index] = columnNameAndValue.get(columnName);
					index++;
				}

			}

			sql.replace(sql.lastIndexOf(","), sql.length(), " ");
			// 主键
			sql.append(" where " + primaryKeyName + "=?");
			parameters[index] = columnNameAndValue.get(primaryKeyName);
			dbUtil= new JDBCUtil();
			//System.out.println(Arrays.toString(parameters)+""+sql);
			if (dbUtil.update(sql.toString(), parameters) > 0) {
				Class<T>  cl=(Class<T>) t.getClass();
				System.out.println(parameters[index]);
				return (T) findPrimaryKey(cl , columnNameAndValue.get(primaryKeyName));
			}

		}
		// System.out.println(sql+"----------------------------------------------");
		return null;
	}

	public  T findPrimaryKey(Class<T> cl, Object primaryKey) {
		// 获得表名
		String tableName = ReflectEntityField.getTableName(cl);
		String primaryKeyName = ReflectEntityField.getPrimaryKeyName(cl);
		if (tableName != null && primaryKeyName != null) {
			String sql = "select *  from  " + tableName + " where " + primaryKeyName + "=?";
			return findSingleBean(sql, cl, primaryKey);
		}
		return null;
	}

	/**
	 * 查询单个对象
	 *
	 * @param sql
	 * @param cl
	 * @return
	 */
	public  T findSingleBean(String sql, Class<T> cl, Object... params) {
		dbUtil= new JDBCUtil();
		List<Map<String, Object>> list = dbUtil.query(sql, params);
		if(list.size()>0)
			return getEntity(list.get(0), cl);
		return  null;
	}
	public <O>  O findSingleBeanOther(String sql, Class<O> cl, Object... params) {
		dbUtil= new JDBCUtil();
		List<Map<String, Object>> list = dbUtil.query(sql, params);
		if(list.size()>0)
			return getLangOrDate(list.get(0), cl);
		return  null;
	}
	/**
	 * 根据主键删除
	 * @param cl
	 * @param primaryKey
	 * @return
	 */
	public  boolean deletePrimaryKey(Class<T> cl, Object primaryKey) {
		dbUtil= new JDBCUtil();
		// 找出表名
		String tableName = ReflectEntityField.getTableName(cl);// admin
		String primaryKeyName =ReflectEntityField.getPrimaryKeyName(cl);// no
		// 判断是否有表名和主键名
		if (tableName != null && primaryKeyName != null) {
			String sql = "delete  from " + tableName + " where " + primaryKeyName + "=?";
			int i = dbUtil.update(sql, primaryKey);
			return i > 0 ? true : false;
		}
		// 找出主键名
		return false;
	}
	/**
	 * 保存实体对象
	 * @param t
	 * @return
	 */
	public  boolean saveEntity(T t) {// inset into table(name,password)
		// value(t.getname,t.password)
		StringBuffer sqlPrefix = new StringBuffer("insert  into ");
		StringBuffer sqlSuffix = new StringBuffer(" value ( ");
		// 表名
		// 找出表名
		String tableName = ReflectEntityField.getTableName(t.getClass());// admin
		// 主键名
		String primaryKeyName = ReflectEntityField.getPrimaryKeyName(t.getClass());// no
		if (tableName != null) {
			if (primaryKeyName != null) {
				sqlPrefix.append(tableName + "(");
				// 主键是否自动增长
				boolean isAutoIncrease = ReflectEntityField.getIsAutoIncrease(t.getClass());
				// 获得所有的 获得所有字段以及该字段的值
				Map<String, Object> columnValue = ReflectEntityField.getColumnValue(t);
				Object[] parameters = null;
				if (isAutoIncrease) {
					parameters = new Object[columnValue.size() - 1];
					int index = 0;
					// 排除主键
					for (String fieldName : columnValue.keySet()) {
						if (!fieldName.equals(primaryKeyName)) {
							// 拼接字段
							sqlPrefix.append(fieldName + ",");
							// 拼接值
							sqlSuffix.append("?,");
							parameters[index] = columnValue.get(fieldName);
							index++;
						}
					}
				} else {
					parameters = new Object[columnValue.size()];
					int index = 0;
					// 排除主键
					for (String fieldName : columnValue.keySet()) {
						// 拼接字段
						sqlPrefix.append(fieldName + ",");
						// 拼接值
						sqlSuffix.append("?,");
						parameters[index] = columnValue.get(fieldName);
						index++;

					}
				}

				// 把最后一个,改成)
				sqlPrefix.setCharAt(sqlPrefix.length() - 1, ')');
				sqlSuffix.setCharAt(sqlSuffix.length() - 1, ')');
				dbUtil= new JDBCUtil();
				int i = dbUtil.update(sqlPrefix.toString() + sqlSuffix, parameters);
				System.out.println(sqlPrefix.toString() + sqlSuffix);

				return i > 0 ? true : false;
			}
		}

		return false;
	}

	/**
	 * 	针对只查询的单个列
	 * @param map
	 * @param cl
	 * @return
	 * @param <O>
	 */
	private <O> O getLangOrDate(Map<String, Object> map, Class<O> cl)  {
			Iterator<String> iterator = map.keySet().iterator();
			 O  o=null;
		     Object value=null;
			 try {
				 if (iterator.hasNext()) {
					  value = map.get(iterator.next());
					 if(value==null)
						 return o;
					 if (value.getClass() == cl) {
						 o = (O) value;
					 } else if (value instanceof Timestamp && cl.getName().equals("java.util.Date")) {
						 o=cl.newInstance();
						 long time = ((Timestamp) value).getTime();
						 Method setTime = cl.getMethod("setTime", long.class);
						 setTime.invoke(o,time);
					 }else if(value instanceof java.sql.Date && cl.getName().equals("java.util.Date")){
						 o=(O)value;
					 }else{
						 System.err.println("数据库类型:"+value.getClass()+"==类型转换错误=="+cl);
					 }
				 }
			 }catch (Exception  e){
				 e.printStackTrace();
				 System.err.println("数据库类型:"+value.getClass()+"==类型转换错误=="+cl);
			 }
	   return   o;
	}

	/**
	 * 利用反射 把每行数据变成对象
	 *
	 * @param resultSet
	 * @param cl
	 * @return
	 */
	private  T getEntity(Map<String,Object> resultSet, Class<T> cl) {
		// 获得对象
		T t = null;
		// 所有属性
		Field[] fields = null;
		// 字段名
		String methodName = null;
		// 属性类型即为 方法类型
		Class<?> methodType = null;
		// 属性的set方法
		Method method = null;
		try {
			//获得对象
			t = cl.newInstance();
			// 获得所有属性
			fields = cl.getDeclaredFields();
			// 循环属性
			for (Field field : fields) {
				// 略过serialVersionUID
				if (field.getName().equals("serialVersionUID")) {
					continue;
				}
				String ColumnName = ReflectEntityField.getColumnName(field);
				if (ColumnName != null) {
					// 利用字段名获得方法名
					methodName = "set" + Character.toUpperCase(field.getName().charAt(0))
							+ field.getName().substring(1);
					// System.err.println(methodName);
					// 方法参数类型 就是字段类型
					methodType = field.getType();
					// System.out.println(field.getType().getSimpleName());
					// 方法参数
					method = cl.getDeclaredMethod(methodName, methodType);

					try {
						// 获得数据库字段的值 属性名即为字段名 利用属性名获得该字段的值
						Object value = resultSet.get(ColumnName);
						// 利用方法赋值
						// method.invoke(t, value);
						setFieldValue(method, t, methodType, value);
					} catch (InvocationTargetException e) {
						System.err.println(cl + "调用的方法出现异常。");
						// e.printStackTrace();
					} catch (IllegalArgumentException e) {
						System.err.println(method + "方法调用 参数 非法或不正确。[数据库字段类型和属性字段类型不一致]");
						// e.printStackTrace();
					}
				}

			}
		} catch (InstantiationException e) {
			System.err.println(cl + "实体类没有无参构造器");
			// e.printStackTrace();
		} catch (IllegalAccessException e) {
			System.err.println(cl + "无妨访问 该构造器或者属性或者方法");
			// e.printStackTrace();
		} catch (NoSuchMethodException e) {
			System.err.println(cl + "实体类没有" + methodName + "方法");
			// e.printStackTrace();
		} catch (SecurityException e) {
			System.err.println(cl + "实体类安全违规。");
			// e.printStackTrace();
		}
		return t;

	}
	private <O> O getEntityOther(Map<String,Object> resultSet, Class<O> cl) {
		// 获得对象
		O t = null;
		// 所有属性
		Field[] fields = null;
		// 字段名
		String methodName = null;
		// 属性类型即为 方法类型
		Class<?> methodType = null;
		// 属性的set方法
		Method method = null;
		try {
			//获得对象
			t = cl.newInstance();
			// 获得所有属性
			fields = cl.getDeclaredFields();
			// 循环属性
			for (Field field : fields) {
				// 略过serialVersionUID
				if (field.getName().equals("serialVersionUID")) {
					continue;
				}
				String ColumnName = ReflectEntityField.getColumnName(field);
				if (ColumnName != null) {
					// 利用字段名获得方法名
					methodName = "set" + Character.toUpperCase(field.getName().charAt(0))
							+ field.getName().substring(1);
					// System.err.println(methodName);
					// 方法参数类型 就是字段类型
					methodType = field.getType();
					// System.out.println(field.getType().getSimpleName());
					// 方法参数
					method = cl.getDeclaredMethod(methodName, methodType);

					try {
						// 获得数据库字段的值 属性名即为字段名 利用属性名获得该字段的值
						Object value = resultSet.get(ColumnName);
						// 利用方法赋值
						// method.invoke(t, value);
						setFieldValue(method, t, methodType, value);
					} catch (InvocationTargetException e) {
						System.err.println(cl + "调用的方法出现异常。");
						// e.printStackTrace();
					} catch (IllegalArgumentException e) {
						System.err.println(method + "方法调用 参数 非法或不正确。[数据库字段类型和属性字段类型不一致]");
						// e.printStackTrace();
					}
				}

			}
		} catch (InstantiationException e) {
			System.err.println(cl + "实体类没有无参构造器");
			// e.printStackTrace();
		} catch (IllegalAccessException e) {
			System.err.println(cl + "无妨访问 该构造器或者属性或者方法");
			// e.printStackTrace();
		} catch (NoSuchMethodException e) {
			System.err.println(cl + "实体类没有" + methodName + "方法");
			// e.printStackTrace();
		} catch (SecurityException e) {
			System.err.println(cl + "实体类安全违规。");
			// e.printStackTrace();
		}
		return t;

	}

	/**
	 * 把每行数据封装成 Object[] 数组
	 * @param resultSet
	 * @return
	 */
	public Object[] getArrayObject(Map<String,Object> resultSet) {
		// 获得一共有几列
		Object[] obj = null;
		obj = new Object[resultSet.size()];
		int index=0;
		for (String column:resultSet.keySet()) {
			obj[index] = resultSet.get(column);
			index++;
		}
		return obj;
	}

	/**
	 * 设置属性值 判断类型是否一致 不一致转化类型
	 * @param method
	 * @param t
	 * @param methodType
	 * @param value
	 * @throws InvocationTargetException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	private void setFieldValue(Method method, Object t, Class<?> methodType, Object value)
			throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{
		//System.out.println(value.getClass() );
		if(value == null) {
			method.invoke(t, value);
		}else if (methodType == value.getClass()) {// 如果数据库字段类型与属性类型一致
			method.invoke(t, value);
		} else {//不一致 强转
			if ((value.getClass() == Long.class || value.getClass().getName().equals("long"))
					&& (methodType == Integer.class || methodType.getName().equals("int"))) {
				method.invoke(t, ((Long) value).intValue());
			} else if ((value.getClass() == Integer.class || value.getClass().getName().equals("int"))
					&& (methodType == Long.class || methodType.getName().equals("long"))) {
				method.invoke(t, ((Integer) value).longValue());
			} else if ((value.getClass() == Double.class || value.getClass().getName().equals("double"))
					&& (methodType == Float.class || methodType.getName().equals("float"))) {
				method.invoke(t, ((Double) value).floatValue());
			} else if ((value.getClass() == Float.class || value.getClass().getName().equals("float"))
					&& (methodType == Double.class || methodType.getName().equals("double"))) {
				method.invoke(t, ((Float) value).doubleValue());
			} else if (value.getClass() == String.class
					&& (methodType == Character.class || methodType.getName().equals("char"))) {
				method.invoke(t, ((String) value).charAt(0));
			} else if ((value.getClass() == String.class && ((String) value).length() == 1)
					&& (methodType == Boolean.class || methodType.getName().equals("boolean"))) {
				method.invoke(t, ((String) value).equals("0"));
			} else if (value.getClass() == java.sql.Date.class && methodType == String.class) {
				method.invoke(t, new SimpleDateFormat("yyyy-MM-dd").format(value));
			}  else if (value.getClass() == java.sql.Date.class && methodType == Date.class) {
				method.invoke(t, value);
			}else if (value.getClass() == String.class && methodType == Date.class) {
				try {
					method.invoke(t, new SimpleDateFormat("yyyy-MM-dd").parse(value.toString()));
				} catch (ParseException e) {
					System.err.println("--日期格式化异常:格式为 yyyy-MM-dd  ---");
					// e.printStackTrace();
				}
				// 包装类对应基本类型Long=long Integer=int
			} else if (value.getClass().getSimpleName().toLowerCase().equals(methodType.getName())
					|| methodType.getSimpleName().toLowerCase().equals(value.getClass().getName())
					|| (value.getClass().getSimpleName().equals("Integer") && methodType.getName().equals("int"))
					|| (methodType.getClass().getSimpleName().equals("Integer")
					&& value.getClass().getName().equals("int"))) {
				method.invoke(t, value);
			} else if (methodType == String.class) {
				method.invoke(t, value.toString());
			}

		} // ..................未完善

	}



}
